#include "../include/CandidateWord.h"
#include "../include/log4cppuse.h"

#include <sstream>
#include <fstream>
#include <algorithm>
#include <unordered_set>
#include <unordered_map>

#include <regex>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
using std::string;
using std::vector;
using std::unordered_set;
using std::unordered_map;

CandidateWordSet::CandidateWordSet()
: m_word("")
, m_cn_sum(0)
, m_en_sum(0)
, m_cn_stop_words(std::make_unique<unordered_set<string>>())
, m_en_stop_words(std::make_unique<unordered_set<string>>())
, m_en_indexes(std::make_shared<map<string, vector<int>>>())
, m_en_dict(std::make_shared<vector<pair<string, int>>>())
, m_cn_indexes(std::make_shared<map<string, vector<int>>>())
, m_cn_dict(std::make_shared<vector<pair<string, int>>>())
, m_indexes_word(std::make_unique<map<string, vector<int>>>())
, m_candidate_words(std::make_shared<priority_queue<Candidate, vector<Candidate>, std::less<Candidate>>>())
{
    load_en_index_set();
    load_cn_index_set();
    load_en_dict_set();
    load_cn_dict_set();
    load_en_stop_word_set();
    load_cn_stop_word_set();
}

CandidateWordSet::~CandidateWordSet() {}

CandidateWordSet &CandidateWordSet::getCandidateWordSet()
{
    static CandidateWordSet CandidateWordSet;
    return CandidateWordSet;
}

string CandidateWordSet::process(const string &word, int topK)
{
    if(word.empty())
    {
        LOG_ERROR("查询词为空:word is empty");
        return "";
    }
    
    // 清空之前的结果
    m_indexes_word->clear();
    m_candidate_words = std::make_shared<priority_queue<Candidate, vector<Candidate>, std::less<Candidate>>>();
    
    setCandiateIndexSet(word);
    string ret = getCandidateWords(topK);
    
    // 清理临时数据
    m_indexes_word->clear();
    m_candidate_words = std::make_shared<priority_queue<Candidate, vector<Candidate>, std::less<Candidate>>>();
    
    return ret;
}

// 返回前topK个候选单词
string CandidateWordSet::getCandidateWords(int topK)
{
    vector<string> candidate_words;
    
    while(candidate_words.size() < topK && !(*m_candidate_words).empty())
    {
        Candidate current = (*m_candidate_words).top();
        
        // 跳过与查询词完全相同的候选词
        if(current.word == m_word)
        {
            (*m_candidate_words).pop();    
            continue;
        }

        // 检查是否已存在（去重）
        auto it = std::find(candidate_words.begin(), candidate_words.end(), current.word);
        if(it != candidate_words.end())
        {
            (*m_candidate_words).pop();
            continue;
        }

        candidate_words.push_back(current.word);
        (*m_candidate_words).pop();
    }
    
    json ret;
    ret["suggestions"] = candidate_words;
    string log = "m_word:" + m_word + " candidate_words:" + ret.dump();
    LOG_DEBUG(log.c_str());
    return ret.dump();
}

// 设置候选索引集
void CandidateWordSet::setCandiateIndexSet(const string &word)
{
    if(word.empty())
    {
        LOG_ERROR("查询词为空:word is empty");
        return;
    }
    m_word = word;
    
    if(m_word[0] >= 'a' && m_word[0] <= 'z' || m_word[0] >= 'A' && m_word[0] <= 'Z')
    {
        LOG_DEBUG("创建英文关键字集合");
        create_en_keyword_set(word);
    } else {
        LOG_DEBUG("创建中文关键字集合");
        create_cn_keyword_set(word);
    }
}

void CandidateWordSet::create_en_keyword_set(const string &word)
{
    // 1. 将关键字拆分成一个一个字符
    vector<string> chars;
    for(char c : word) {
        if(isalpha(c)) {  // 只处理字母字符
            chars.push_back(string(1, tolower(c)));  // 转换为小写
        }
    }
    
    // 2. 通过索引库获取每一个字符对应的词语集合
    unordered_set<int> all_line_numbers;  // 存储所有行号（去重）
    
    for(const auto& ch : chars) {
        auto it = m_en_indexes->find(ch);
        if(it != m_en_indexes->end()) {
            // 将该字符对应的所有行号加入集合
            for(int line_num : it->second) {
                all_line_numbers.insert(line_num);
            }
        }
    }
    
    // 3. 从字典中获取候选词并计算编辑距离
    unordered_set<string> added_words;  // 用于去重
    
    for(int line_num : all_line_numbers) {
        if(line_num < 0 || line_num >= m_en_dict->size()) {
            continue;
        }
        
        const auto& dict_entry = (*m_en_dict)[line_num];
        const string& candidate_word = dict_entry.first;
        int frequency = dict_entry.second;
        
        // 去重检查
        if(added_words.find(candidate_word) != added_words.end()) {
            continue;
        }
        added_words.insert(candidate_word);
        
        // 检查是否在停用词表中
        if(m_en_stop_words->find(candidate_word) != m_en_stop_words->end()) {
            continue;
        }
        
        // 计算编辑距离
        int edit_distance = CalculateEditDistance(candidate_word, m_word);
        
        // 创建候选词对象并加入优先队列
        Candidate candidate = {candidate_word, edit_distance, frequency};
        (*m_candidate_words).push(candidate);
    }
    
    LOG_DEBUG(("创建英文关键字集合，共添加 " + std::to_string(added_words.size()) + " 个候选词").c_str());
}

void CandidateWordSet::create_cn_keyword_set(const string &word)
{
    // 1. 将关键字拆分成一个一个字符（中文字符）
    vector<string> chars;
    
    // 简单的中文字符拆分（假设每个中文字符都是3字节UTF-8）
    for(size_t i = 0; i < word.length(); ) {
        if((word[i] & 0x80) == 0) {
            // ASCII字符，跳过
            i++;
        } else {
            // 中文字符，取3字节
            if(i + 2 < word.length()) {
                chars.push_back(word.substr(i, 3));
                i += 3;
            } else {
                break;
            }
        }
    }
    
    // 2. 通过索引库获取每一个字符对应的词语集合
    unordered_set<int> all_line_numbers;  // 存储所有行号（去重）
    
    for(const auto& ch : chars) {
        auto it = m_cn_indexes->find(ch);
        if(it != m_cn_indexes->end()) {
            // 将该字符对应的所有行号加入集合
            for(int line_num : it->second) {
                all_line_numbers.insert(line_num);
            }
        }
    }
    
    // 3. 从字典中获取候选词并计算编辑距离
    unordered_set<string> added_words;  // 用于去重
    
    for(int line_num : all_line_numbers) {
        if(line_num < 0 || line_num >= m_cn_dict->size()) {
            continue;
        }
        
        const auto& dict_entry = (*m_cn_dict)[line_num];
        const string& candidate_word = dict_entry.first;
        int frequency = dict_entry.second;
        
        // 去重检查
        if(added_words.find(candidate_word) != added_words.end()) {
            continue;
        }
        added_words.insert(candidate_word);
        
        // 检查是否在停用词表中
        if(m_cn_stop_words->find(candidate_word) != m_cn_stop_words->end()) {
            continue;
        }
        
        // 计算编辑距离
        int edit_distance = CalculateEditDistance(candidate_word, m_word);
        
        // // 创建候选词对象并加入优先队列---我们只要10个候选词-堆的大小就是10就可以了
        // if((*m_candidate_words).size() >= 10) {
        //     // 如果当前编辑距离大于等于堆顶元素的编辑距离，直接跳过
        //     if(edit_distance >= (*m_candidate_words).top().edit_distance) {
        //         continue;
        //     }
        //     // 弹出堆顶元素（编辑距离最大的）
        //     (*m_candidate_words).pop();
        // }

        Candidate candidate = {candidate_word, edit_distance, frequency};
        (*m_candidate_words).push(candidate);
    }
    
    LOG_DEBUG(("创建中文关键字集合，共添加 " + std::to_string(added_words.size()) + " 个候选词").c_str());
}

void CandidateWordSet::load_en_index_set(const string &index_en_lib_file)
{
    string log = "加载英文索引库: " + index_en_lib_file;
    LOG_DEBUG(log.c_str());

    std::ifstream index_file(index_en_lib_file);
    if(!index_file.is_open())
    {   
        std::string log = "打开英文索引库文件 " + index_en_lib_file + " 失败";
        LOG_ERROR(log.c_str());
        return;
    }
    
    std::string line;
    int line_count = 0;
    
    while(std::getline(index_file, line))
    {
        std::istringstream iss(line);
        std::string first_char;
        
        // 首先读取第一个字符（键）
        if (!std::getline(iss, first_char, ' ')) {
            continue; // 跳过空行
        }
        
        std::string index_str;
        while (std::getline(iss, index_str, ' ')) 
        {
            if(!index_str.empty())
            {
                try {
                    int index = std::stoi(index_str);
                    (*m_en_indexes)[first_char].push_back(index);
                } catch(const std::exception& e) {
                    // 忽略转换错误
                    continue;
                }
            }
        }
        line_count++;
    }
    index_file.close();
    
    string debug_msg = "加载英文索引库 " + index_en_lib_file + " 完成，共 " + 
                       std::to_string(line_count) + " 行，" + 
                       std::to_string(m_en_indexes->size()) + " 个字符";
    LOG_DEBUG(debug_msg.c_str());
}   

void CandidateWordSet::load_cn_index_set(const string &index_cn_lib_file)
{
    string log = "加载中文索引库: " + index_cn_lib_file;
    LOG_DEBUG(log.c_str());

    std::ifstream index_file(index_cn_lib_file);
    if(!index_file.is_open())
    {   
        std::string log = "打开中文索引库文件 " + index_cn_lib_file + " 失败";
        LOG_ERROR(log.c_str());
        return; 
    }

    std::string line;
    int line_count = 0;
    
    while(std::getline(index_file, line))
    {
        std::istringstream iss(line);
        std::string first_char;
        
        // 首先读取第一个字符（键）
        if (!std::getline(iss, first_char, ' ')) {
            continue; // 跳过空行
        }
        
        std::string index_str;
        while (std::getline(iss, index_str, ' ')) 
        {
            if(!index_str.empty())
            {
                try {
                    int index = std::stoi(index_str);
                    (*m_cn_indexes)[first_char].push_back(index);
                } catch(const std::exception& e) {
                    // 忽略转换错误
                    continue;
                }
            }
        }
        line_count++;
    }
    index_file.close();
    
    string debug_msg = "加载中文索引库 " + index_cn_lib_file + " 完成，共 " + 
                       std::to_string(line_count) + " 行，" + 
                       std::to_string(m_cn_indexes->size()) + " 个字符";
    LOG_DEBUG(debug_msg.c_str());
}

void CandidateWordSet::load_en_dict_set(const string &dict_en_lib_file)
{
    string log = "加载英文字典: " + dict_en_lib_file;
    LOG_DEBUG(log.c_str());

    std::ifstream dict_file(dict_en_lib_file);
    if(!dict_file.is_open())
    {   
        std::string log = "打开英文字典文件 " + dict_en_lib_file + " 失败";
        LOG_ERROR(log.c_str());
        return;
    }
    
    string line;
    int line_count = 0;
    
    while (std::getline(dict_file, line))
    {
        std::istringstream iss(line);
        std::string word;
        int frequency;
        
        if(iss >> word >> frequency) {
            m_en_dict->push_back({word, frequency});
            m_en_sum += frequency;
            line_count++;
        }
    }
    
    string debug_msg = "加载英文字典库 " + dict_en_lib_file + " 完成，共 " + 
                       std::to_string(line_count) + " 行，" + 
                       std::to_string(m_en_dict->size()) + " 个单词";
    LOG_DEBUG(debug_msg.c_str());
}

void CandidateWordSet::load_cn_dict_set(const string &dict_cn_lib_file)
{
    string log = "加载中文字典: " + dict_cn_lib_file;
    LOG_DEBUG(log.c_str());

    std::ifstream dict_file(dict_cn_lib_file);
    if(!dict_file.is_open())
    {   
        std::string log = "打开中文字典文件 " + dict_cn_lib_file + " 失败";
        LOG_ERROR(log.c_str());
        return;
    }
    
    string line;
    int line_count = 0;
    
    while (std::getline(dict_file, line))
    {
        std::istringstream iss(line);
        std::string word;
        int frequency;  
        
        if(iss >> word >> frequency) {
            m_cn_dict->push_back({word, frequency});
            m_cn_sum += frequency;
            line_count++;
        }
    }
    
    string debug_msg = "加载中文字典库 " + dict_cn_lib_file + " 完成，共 " + 
                       std::to_string(line_count) + " 行，" + 
                       std::to_string(m_cn_dict->size()) + " 个单词";
    LOG_DEBUG(debug_msg.c_str());
}

void CandidateWordSet::load_cn_stop_word_set(const string &stop_word_lib_file)
{
    string log = "加载中文字停词库: " + stop_word_lib_file;
    LOG_DEBUG(log.c_str());

    std::ifstream stop_word_file(stop_word_lib_file);
    if(!stop_word_file.is_open())
    {   
        std::string log = "打开中文字停词库文件 " + stop_word_lib_file + " 失败";
        LOG_ERROR(log.c_str());
        return;
    }
    
    string line;
    int line_count = 0;
    
    while (std::getline(stop_word_file, line))
    {
        if(!line.empty())
        {
            m_cn_stop_words->insert(line);
            line_count++;
        }
    }
    
    string debug_msg = "加载中文字停词库 " + stop_word_lib_file + " 完成，共 " + 
                       std::to_string(line_count) + " 行，" + 
                       std::to_string(m_cn_stop_words->size()) + " 个停词";
    LOG_DEBUG(debug_msg.c_str());
}

void CandidateWordSet::load_en_stop_word_set(const string &stop_word_lib_file)
{
    string log = "加载英文停词库: " + stop_word_lib_file;
    LOG_DEBUG(log.c_str());

    std::ifstream stop_word_file(stop_word_lib_file);
    if(!stop_word_file.is_open())
    {   
        std::string log = "open stop word file " + stop_word_lib_file + " failed";
        LOG_ERROR(log.c_str());
        return;
    }
    
    string line;
    int line_count = 0;
    
    while (std::getline(stop_word_file, line))
    {
        if(!line.empty())
        {
            m_en_stop_words->insert(line);
            line_count++;
        }
    }
    
    string debug_msg = "加载英文停词库 " + stop_word_lib_file + " 完成，共 " + 
                       std::to_string(line_count) + " 行，" + 
                       std::to_string(m_en_stop_words->size()) + " 个停词";
    LOG_DEBUG(debug_msg.c_str());
}

int CandidateWordSet::CalculateEditDistance(const string word1, const string word2)
{
    int m = word1.length();
    int n = word2.length();
    
    // 创建DP表
    std::vector<std::vector<int>> dp(m + 1, std::vector<int>(n + 1, 0));
    
    // 初始化边界条件
    for (int i = 0; i <= m; i++) {
        dp[i][0] = i;  // 删除所有字符
    }
    for (int j = 0; j <= n; j++) {
        dp[0][j] = j;  // 插入所有字符
    }
    
    // 填充DP表
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            if (word1[i - 1] == word2[j - 1]) {
                // 字符相同，不需要操作
                dp[i][j] = dp[i - 1][j - 1];
            } else {
                // 取三种操作的最小值
                dp[i][j] = std::min({
                    dp[i - 1][j] + 1,      // 删除 word1[i-1]
                    dp[i][j - 1] + 1,      // 在word1中插入 word2[j-1]
                    dp[i - 1][j - 1] + 1   // 将 word1[i-1] 替换为 word2[j-1]
                });
            }
        }
    }
    
    return dp[m][n];
}

#if 0
int main(int argc, char const *argv[])
{  
    while(true)
    {
        string word;
        std::cout<<"请输入要查询的单词:"<<std::endl;
        std::cin>>word;
        CandidateWordSet& candidateWordSet = CandidateWordSet::getCandidateWordSet();
        string ret = candidateWordSet.process(word);
        
        std::cout<<ret<<std::endl;  
    }
}
#endif